# -*- coding: utf-8 -*-

#
# peerscape.http.controllers.post
#
# Copyright 2008 Helsinki Institute for Information Technology
# and the authors.
#
# Authors:
#       Konrad Markus <konrad.markus@hiit.fi>
#

# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation files
# (the "Software"), to deal in the Software without restriction,
# including without limitation the rights to use, copy, modify, merge,
# publish, distribute, sublicense, and/or sell copies of the Software,
# and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

from __future__ import with_statement

import os
import logging
import cgi
import urllib
import re
import uuid

from webob import Response
from webob.exc import HTTPForbidden, HTTPBadRequest, HTTPOk

from peerscape.lib import json
from peerscape.http.controllers import BaseController

class Post(BaseController):
    def index(self, req):
        if req.method == 'POST':
            db = req.environ['peerscape.db']
            iid = req.urlvars.get('peerscape.hexlabel')
            path = urllib.unquote(req.urlvars.get('peerscape.path'))

            if not iid or not path:
                return HTTPBadRequest()

            path = '/'+path
            if path.find('$uiid'):
                # substitute $uiid in the path with a generated uiid
                u = uuid.uuid4()
                p = re.compile('\$uuid')
                path = p.sub(str(u), path)

            dom_cipher = None
            if req.POST.get('auto_crypt'):
                try:
                    dom_cipher = db.get_group_cipher(iid)
                except LookupError:
                    assert dom_cipher == None
                    pass

            # write value
            ret = path
            if req.POST.has_key('value'):
                try:
                    if req.headers.get('content-type') and req.headers['content-type'].startswith('multipart/form-data'):
                        # file upload
                        upload = req.POST['value']
                        if upload.__class__ == cgi.FieldStorage:
                            if hasattr(upload.file, 'fileno'): 
                                size = os.fstat(upload.file.fileno())[6] 
                            else: 
                                upload.file.seek(0, 2) 
                                size = upload.file.tell() 
                                upload.file.seek(0) 


                            if size == 0: 
                                value = ''
                            else:
                                value = upload.file.read()
                                upload.file.close()

                            # if a file upload substitute $ext in the path with the original file extension of the uploaded file
                            if path.find('\$ext'):
                                head, ext = os.path.splitext(upload.filename)
                                if ext:
                                    if ext.startswith('.'):
                                        ext = ext[1:]

                                    p = re.compile('\$ext')
                                    path = p.sub(str(ext), path)

                            if req.POST.has_key('escape_html'):
                                value = cgi.escape(value)

                            if req.POST.get('extended_ret'):
                                ret = json.dumps({'path': path, 'size': size, 'ext': ext, 'original_filename': upload.filename})
                            else:
                                ret = path

                            db.put(iid, path, value, cipher=dom_cipher)
                    else:
                        value = req.POST['value']
                        if req.POST.get('insertpath'):
                            # insertpath handling; assumes we are working with json values
                            insertpath = req.POST['insertpath']

                            # strip out the action from the beginning of the insertpath
                            # action an be one of: append, prepend [for list targets] or set
                            action = insertpath[0:4]
                            if action in ['$app', '$pre', '$set']:
                                insertpath = insertpath[4:]
                            else:
                                return HTTPBadRequest()

                            # get the current json value
                            cur_value = json.loads(db.get(iid, path))
                            if cur_value == None:
                                # insertpath cannot work on a non-existent value
                                return HTTPBadRequest()

                            try:
                                # evaluate the path against the cur value
                                (stack, cur, oB) = ([], '', False)
                                for c in insertpath:
                                    if c == '[':
                                        oB = True
                                        if cur:
                                            stack.append(cur)
                                            cur = ''

                                    elif c == ']':
                                        if cur:
                                            # only integer indices for lists can be used in square bracket notation
                                            stack.append(int(cur))
                                            cur = ''
                                        oB = False

                                    elif c == '.':
                                        if cur:
                                            stack.append(cur)
                                            cur = ''
                                    else:
                                        cur = cur + c

                                # don't forget the last element if any
                                if cur:
                                    stack.append(cur)

                                # reserve the last element of a $set stack
                                if action == '$set':
                                    last = stack.pop()

                                # step through structure
                                stackp = cur_value
                                for e in stack:
                                    stackp = stackp[e]

                                # update the cur value according to action
                                # make sure it is dealing with json properly
                                try:
                                    value = json.loads(value)
                                except:
                                    # assume value is a single scalar string
                                    pass

                                if action == '$app':
                                    stackp.append(value)
                                elif action == '$pre':
                                    stackp.insert(0, value)
                                else:
                                    # '$set'
                                    stackp[last] = value

                                # set the value
                                if req.POST.has_key('escape_html'):
                                    value = cgi.escape(value)

                                db.put(iid, path, json.dumps(cur_value), cipher=dom_cipher)
                            except:
                                logging.exception('post: error.')
                                return HTTPBadRequest()
                        else:
                            if req.POST.has_key('escape_html'):
                                value = cgi.escape(value)

                            db.put(iid, path, value, cipher=dom_cipher)
                    
                    # form a success response, including any jsonp
                    res = HTTPOk()
                    if req.POST.get('extended_ret'):
                        if req.POST.has_key('jsonp'):
                            res.body = '%s(%s)' % (req.POST['jsonp'], ret)
                        else:
                            res.body = '(%s)' % ret
                    else:
                        if req.POST.has_key('jsonp'):
                            res.body = '%s("%s")' % (req.POST['jsonp'], ret)
                        else:
                            res.body = '("%s")' % ret
                    return res
                except:
                    logging.exception('post: error.')
                    return HTTPForbidden()
            else:
                return HTTPBadRequest()
        else:
            return HTTPBadRequest()
            
